home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 March / CMCD0305.ISO / Software / Freeware / Programare / nvu / nvu-0.80-win32-installer-full.exe / {app} / chrome / cascades.jar / content / cascades / EdCssProps.js < prev    next >
Text File  |  2005-01-31  |  67KB  |  1,749 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is CaScadeS, a stylesheet editor for Composer.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Daniel Glazman.
  18.  * Portions created by the Initial Developer are Copyright (C) 2002
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Original author: Daniel Glazman <daniel@glazman.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const SHEET        = 1;
  39. const STYLE_RULE   = 2;
  40. const IMPORT_RULE  = 3;
  41. const MEDIA_RULE   = 4;
  42. const CHARSET_RULE = 5;
  43. const PAGE_RULE    = 6;
  44. const OWNER_NODE   = 7;
  45.  
  46. // const COMPATIBILITY_TAB = 1;
  47. const GENERAL_TAB       = 2;
  48. const TEXT_TAB          = 3;
  49. const BACKGROUND_TAB    = 4;
  50. const BORDER_TAB        = 5;
  51. const BOX_TAB           = 6;
  52. const AURAL_TAB         = 7;
  53.  
  54. const GENERIC_SELECTOR      = 0;
  55. const TYPE_ELEMENT_SELECTOR = 1;
  56. const CLASS_SELECTOR        = 2;
  57.  
  58. const kAsyncTimeout = 1500; // 1.5 second
  59.  
  60. var objectsArray = null;
  61. var gTimerID;
  62. var gAsyncLoadingTimerID;
  63.  
  64. // needed for commonCssProps.js
  65. var gHaveDocumentUrl = false;
  66.  
  67. var gInsertIndex = -1;
  68.  
  69. // * dialog initialization code
  70. function Startup()
  71. {
  72.   // are we in a pre-1.3 Mozilla ?
  73.   if (typeof window.InitEditorShell == "function") {
  74.     // yes, so let's get an editorshell
  75.     if (!InitEditorShell())
  76.       return;
  77.   }
  78.   else if (typeof window.GetCurrentEditor != "function" || !GetCurrentEditor()) {
  79.     window.close();
  80.     return;
  81.   }
  82.  
  83.   // gDialog is declared in EdDialogCommon.js
  84.  
  85.   gDialog.selectionBased = false;
  86.  
  87.   // Set commonly-used widgets like this:
  88.   gDialog.selectedTab = TEXT_TAB;
  89.   gDialog.sheetsTreechildren            = document.getElementById("stylesheetsTree");
  90.   gDialog.sheetsTree                    = document.getElementById("sheetsTree");
  91.   gDialog.sheetInfoTab                  = document.getElementById("sheetInfoTab");
  92.   gDialog.atimportButton                = document.getElementById("atimportButton");
  93.   gDialog.atmediaButton                 = document.getElementById("atmediaButton");
  94.   gDialog.linkButton                    = document.getElementById("linkButton");
  95.   gDialog.styleButton                   = document.getElementById("styleButton");
  96.   gDialog.ruleButton                    = document.getElementById("ruleButton");
  97.   gDialog.removeButton                  = document.getElementById("removeButton");
  98.   gDialog.upButton                      = document.getElementById("upButton");
  99.   gDialog.downButton                    = document.getElementById("downButton");
  100.  
  101.   gDialog.selectedTab = GENERAL_TAB;
  102.   gDialog.sheetInfoTabPanelTitle        = document.getElementById("sheetInfoTabPanelTitle");
  103.   gDialog.textTab                       = document.getElementById("textTab");
  104.   gDialog.brownFoxLabel                 = document.getElementById("brownFoxLabel");
  105.   gDialog.backgroundImageInput          = document.getElementById("backgroundImageInput");
  106.   gDialog.backgroundPreview             = document.getElementById("backgroundPreview");
  107.   gDialog.sheetTabbox                   = document.getElementById("sheetTabbox");
  108.   gDialog.backgroundColorInput          = document.getElementById("backgroundColorInput");
  109.   gDialog.textColorInput                = document.getElementById("textColorInput");
  110.   gDialog.backgroundRepeatMenulist      = document.getElementById("backgroundRepeatMenulist");
  111.   gDialog.backgroundAttachmentCheckbox  = document.getElementById("backgroundAttachmentCheckbox");
  112.   gDialog.xBackgroundPositionRadiogroup = document.getElementById("xBackgroundPositionRadiogroup");
  113.   gDialog.yBackgroundPositionRadiogroup = document.getElementById("yBackgroundPositionRadiogroup");
  114.   gDialog.fontFamilyRadiogroup          = document.getElementById("fontFamilyRadiogroup");
  115.   gDialog.customFontFamilyInput         = document.getElementById("customFontFamilyInput");
  116.   gDialog.predefFontFamilyMenulist      = document.getElementById("predefFontFamilyMenulist");
  117.   gDialog.fontSizeInput                 = document.getElementById("fontSizeInput");
  118.   gDialog.lineHeightInput               = document.getElementById("lineHeightInput");
  119.   gDialog.textUnderlineCheckbox         = document.getElementById("underlineTextDecorationCheckbox");
  120.   gDialog.textOverlineCheckbox          = document.getElementById("overlineTextDecorationCheckbox");
  121.   gDialog.textLinethroughCheckbox       = document.getElementById("linethroughTextDecorationCheckbox");
  122.   gDialog.textBlinkCheckbox             = document.getElementById("blinkTextDecorationCheckbox");
  123.   gDialog.noDecorationCheckbox          = document.getElementById("noneTextDecorationCheckbox");
  124.  
  125.   gDialog.topBorderStyleMenulist        = document.getElementById("topBorderStyleMenulist");
  126.   gDialog.topBorderWidthInput           = document.getElementById("topBorderWidthInput");
  127.   gDialog.topBorderColorInput           = document.getElementById("topBorderColorInput");
  128.  
  129.   gDialog.leftBorderStyleMenulist       = document.getElementById("leftBorderStyleMenulist");
  130.   gDialog.leftBorderWidthInput          = document.getElementById("leftBorderWidthInput");
  131.   gDialog.leftBorderColorInput          = document.getElementById("leftBorderColorInput");
  132.  
  133.   gDialog.rightBorderStyleMenulist      = document.getElementById("rightBorderStyleMenulist");
  134.   gDialog.rightBorderWidthInput         = document.getElementById("rightBorderWidthInput");
  135.   gDialog.rightBorderColorInput         = document.getElementById("rightBorderColorInput");
  136.  
  137.   gDialog.bottomBorderStyleMenulist     = document.getElementById("bottomBorderStyleMenulist");
  138.   gDialog.bottomBorderWidthInput        = document.getElementById("bottomBorderWidthInput");
  139.   gDialog.bottomBorderColorInput        = document.getElementById("bottomBorderColorInput");
  140.  
  141.   gDialog.allFourBordersSame            = document.getElementById("allFourBordersSame");
  142.   gDialog.borderPreview                 = document.getElementById("borderPreview");
  143.  
  144.   gDialog.volumeScrollbar               = document.getElementById("volumeScrollbar");
  145.   gDialog.volumeMenulist                = document.getElementById("volumeMenulist");
  146.   gDialog.muteVolumeCheckbox            = document.getElementById("muteVolumeCheckbox");
  147.  
  148.   gDialog.opacityScrollbar              = document.getElementById("opacityScrollbar");
  149.   gDialog.opacityLabel                  = document.getElementById("opacityLabel");
  150.  
  151.   gDialog.sheetInfoTabGridRows          = document.getElementById("sheetInfoTabGridRows");
  152.   gDialog.sheetInfoTabGrid              = document.getElementById("sheetInfoTabGrid");
  153.  
  154.   gDialog.expertMode = true;
  155.   gDialog.modified = false;
  156.   gDialog.selectedIndex = -1;
  157.  
  158.   gDialog.bundle                        = document.getElementById("cascadesBundle");
  159.  
  160.   gHaveDocumentUrl = GetDocumentBaseUrl();
  161.  
  162.   // Initialize all dialog widgets here,
  163.   // e.g., get attributes from an element for property dialog
  164.   InitSheetsTree(gDialog.sheetsTreechildren);
  165.   
  166.   // Set window location relative to parent window (based on persisted attributes)
  167.   SetWindowLocation();
  168. }
  169.  
  170. // * Toggles on/off expert mode. In expert mode, all buttons are enabled
  171. //   including buttons for stylesheet creation. When the mode is off, only
  172. //   the "create rule" button is enabled, a stylesheet being created to contain
  173. //   the new rule if necessary
  174. function toggleExpertMode()
  175. {
  176.   // toggle the boolean
  177.   gDialog.expertMode = !gDialog.expertMode;
  178.  
  179.   if (gDialog.expertMode) {
  180.     if (gDialog.selectedIndex == -1) {
  181.       // if expert mode is on but no selection in the tree, only
  182.       // sheet creation buttons are enabled
  183.       UpdateButtons(false, false, true, true, false, false);
  184.     }
  185.     else {
  186.       // if expert mode is on and we have something selected in the tree,
  187.       // the state of the buttons depend on the type of the selection
  188.       var external  = objectsArray[gDialog.selectedIndex].external;
  189.       var type      = objectsArray[gDialog.selectedIndex].type;
  190.       UpdateButtons(!external, !external, true, true, !external, (!external || (type == SHEET)) );
  191.     }
  192.   }
  193.   else {
  194.     // if we're not in expert mode, allow only rule creation button
  195.     UpdateButtons(!gDialog.atimportButton.hasAttribute("disabled"),
  196.                   !gDialog.atmediaButton.hasAttribute("disabled"),
  197.                   !gDialog.linkButton.hasAttribute("disabled"),
  198.                   !gDialog.styleButton.hasAttribute("disabled"),
  199.                   !gDialog.ruleButton.hasAttribute("disabled"),
  200.                   !gDialog.removeButton.hasAttribute("disabled"));
  201.   }
  202. }
  203.  
  204. // * This function recreates the contents of the STYLE elements and
  205. //   of the stylesheets local to the filesystem
  206. function FlushChanges()
  207. {
  208.   if (gDialog.modified) {
  209.     // let's make sure the editor is going to require save on exit
  210.     getCurrentEditor().incrementModificationCount(1);
  211.   }
  212.   // Validate all user data and set attributes and possibly insert new element here
  213.   // If there's an error the user must correct, return false to keep dialog open.
  214.   var sheet;
  215.   for (var i = 0; i < objectsArray.length; i++) {
  216.     if (objectsArray[i].modified && !objectsArray[i].external &&
  217.         objectsArray[i].type == SHEET) {
  218.       /* let's serialize this stylesheet ! */
  219.       sheet = objectsArray[i].cssElt;
  220.       if (sheet.ownerNode.nodeName.toLowerCase() == "link")
  221.         SerializeExternalSheet(sheet, null);
  222.       else
  223.         SerializeEmbeddedSheet(sheet);
  224.     }
  225.   }
  226.   SaveWindowLocation();
  227.   return true; // do close the window
  228. }
  229.  
  230. // * removes all the content in a tree
  231. //   param XULElement sheetsTree
  232. function CleanSheetsTree(sheetsTreeChildren)
  233. {
  234.   // we need to clear the selection in the tree otherwise the onselect
  235.   // action on the tree will be fired when we removed the selected entry
  236.   ClearTreeSelection(gDialog.sheetsTree);
  237.  
  238.   var elt = sheetsTreeChildren.firstChild;
  239.   while (elt) {
  240.     var tmp = elt.nextSibling;
  241.     sheetsTreeChildren.removeChild(elt);
  242.     elt = tmp;
  243.   }
  244. }
  245.  
  246. function AddSheetEntryToTree(sheetsTree, ownerNode)
  247. {
  248.   if (ownerNode.nodeType == Node.ELEMENT_NODE) {
  249.     var ownerTag  = ownerNode.nodeName.toLowerCase()
  250.     var relType   = ownerNode.getAttribute("rel");
  251.     if (relType) relType = relType.toLowerCase();
  252.     if (ownerTag == "style" ||
  253.         (ownerTag == "link" && relType.indexOf("stylesheet") != -1)) {
  254.  
  255.       var treeitem  = document.createElementNS(XUL_NS, "treeitem");
  256.       var treerow   = document.createElementNS(XUL_NS, "treerow");
  257.       var treecell  = document.createElementNS(XUL_NS, "treecell");
  258.  
  259.       // what kind of owner node do we have here ?
  260.       // a style element indicates an embedded stylesheet,
  261.       // while a link element indicates an external stylesheet;
  262.       // the case of an XML Processing Instruction is not handled, we
  263.       // are supposed to be in HTML 4
  264.       var external = false;
  265.       if (ownerTag == "style") {
  266.         treecell.setAttribute("label", gDialog.bundle.getString("InternalStylesheet"));
  267.       }
  268.       else if (ownerTag == "link") {
  269.         // external stylesheet, let's present its URL to user
  270.         treecell.setAttribute("label", StripUsernamePassword(ownerNode.href));
  271.         external = true;
  272.         if ( /(\w*):.*/.test(ownerNode.href) ) {
  273.           if (RegExp.$1 == "file") {
  274.             external = false;
  275.           }
  276.         }
  277.         else
  278.           external = false;
  279.       }
  280.       // add a new entry to the tree
  281.       var o = newObject( treeitem, external, SHEET, ownerNode.sheet, false, 0 );
  282.       PushInObjectsArray(o);
  283.       
  284.       treerow.appendChild(treecell);
  285.       treeitem.appendChild(treerow);
  286.       treeitem.setAttribute("container", "true");
  287.       // add enties to the tree for the rules in the current stylesheet
  288.       var rules = null;
  289.       if (ownerNode.sheet)
  290.         rules = ownerNode.sheet.cssRules;
  291.       AddRulesToTreechildren(treeitem, rules, external, 1);
  292.  
  293.       sheetsTree.appendChild(treeitem);
  294.     }
  295.   }
  296. }
  297.  
  298. function PushInObjectsArray(o)
  299. {
  300.   if (gInsertIndex == -1)
  301.     objectsArray.push(o);
  302.   else {
  303.     objectsArray.splice(gInsertIndex, 0, o);
  304.     gInsertIndex++;
  305.   }
  306. }
  307.  
  308. // * populates the tree in the dialog with entries
  309. //   corresponding to all stylesheets and css rules attached to
  310. //   document
  311. //   param XULElement sheetsTree
  312. function InitSheetsTree(sheetsTree)
  313. {
  314.   // remove all entries in the tree
  315.   CleanSheetsTree(sheetsTree);
  316.   // Look for the stylesheets attached to the current document
  317.   // Get them from the STYLE and LINK elements because of async sheet loading :
  318.   // the LINK element is always here while the corresponding sheet might be
  319.   // delayed by network
  320.   var headNode = GetHeadElement();
  321.   if ( headNode && headNode.hasChildNodes() ) {
  322.     var ssn = headNode.childNodes.length;
  323.     objectsArray = new Array();
  324.     if (ssn) {
  325.       var i;
  326.       gInsertIndex = -1;
  327.       for (i=0; i<ssn; i++) {
  328.         var ownerNode = headNode.childNodes[i];
  329.         AddSheetEntryToTree(sheetsTree, ownerNode); 
  330.       }
  331.     }
  332.   }
  333. }
  334.  
  335. // * create a new "object" corresponding to an entry in the tree
  336. //   unfortunately, it's still impossible to attach a JS object to
  337. //   a treecell :-(
  338. //   param XULElement xulElt
  339. //   param boolean external
  340. //   param integer type
  341. //   param (DOMNode|DOMCSSStyleSheet|DOMCSSRule) cssElt
  342. //   param boolean modified
  343. //   param integer depth
  344. function newObject( xulElt, external, type, cssElt, modified, depth)
  345. {
  346.   return {xulElt:xulElt,
  347.           external:external,
  348.           type:type,
  349.           cssElt:cssElt,
  350.           modified:modified,
  351.           depth:depth};
  352. }
  353.  
  354. function AddStyleRuleToTreeChildren(rule, external, depth)
  355. {
  356.   var subtreeitem  = document.createElementNS(XUL_NS, "treeitem");
  357.   var subtreerow   = document.createElementNS(XUL_NS, "treerow");
  358.   var subtreecell  = document.createElementNS(XUL_NS, "treecell");
  359.   // show the selector attached to the rule
  360.   subtreecell.setAttribute("label", rule.selectorText);
  361.   var o = newObject( subtreeitem, external, STYLE_RULE, rule, false, depth );
  362.   PushInObjectsArray(o);
  363.   if (external) {
  364.     subtreecell.setAttribute("properties", "external");
  365.   }
  366.   subtreerow.appendChild(subtreecell);
  367.   subtreeitem.appendChild(subtreerow);
  368.  
  369.   return subtreeitem;
  370. }
  371.  
  372. function AddPageRuleToTreeChildren(rule, external, depth)
  373. {
  374.   var subtreeitem  = document.createElementNS(XUL_NS, "treeitem");
  375.   var subtreerow   = document.createElementNS(XUL_NS, "treerow");
  376.   var subtreecell  = document.createElementNS(XUL_NS, "treecell");
  377.   // show the selector attached to the rule
  378.   subtreecell.setAttribute("label", "@page " + rule.selectorText);
  379.   var o = newObject( subtreeitem, external, PAGE_RULE, rule, false, depth );
  380.   PushInObjectsArray(o);
  381.   if (external) {
  382.     subtreecell.setAttribute("properties", "external");
  383.   }
  384.   subtreerow.appendChild(subtreecell);
  385.   subtreeitem.appendChild(subtreerow);
  386.  
  387.   return subtreeitem;
  388. }
  389.  
  390. function AddImportRuleToTreeChildren(rule, external, depth)
  391. {
  392.   var subtreeitem  = document.createElementNS(XUL_NS, "treeitem");
  393.   var subtreerow   = document.createElementNS(XUL_NS, "treerow");
  394.   var subtreecell  = document.createElementNS(XUL_NS, "treecell");
  395.   // show "@import" and the URL
  396.   subtreecell.setAttribute("label", "@import "+rule.href, external);
  397.   var o = newObject( subtreeitem, external, IMPORT_RULE, rule, false, depth );
  398.   PushInObjectsArray(o);
  399.   if (external) {
  400.     subtreecell.setAttribute("properties", "external");
  401.   }
  402.   subtreerow.appendChild(subtreecell);
  403.   subtreeitem.appendChild(subtreerow);
  404.   subtreeitem.setAttribute("container", "true");
  405.   if (rule.styleSheet) {
  406.     // if we have a stylesheet really imported, let's browse it too
  407.     // increasing the depth and marking external
  408.     AddRulesToTreechildren(subtreeitem , rule.styleSheet.cssRules, true, depth+1);
  409.   }
  410.   return subtreeitem;
  411. }
  412.  
  413. // * adds subtreeitems for the CSS rules found
  414. //   into rules; in case of an at-rule, the method calls itself with
  415. //   a subtreeitem, the rules in the at-rule, a boolean specifying if
  416. //   the rules are external to the document or not, and an increased
  417. //   depth.
  418. //   param XULElement  treeItem
  419. //   param CSSRuleList rules
  420. //   param boolean     external
  421. //   param integer     depth
  422. function AddRulesToTreechildren(treeItem, rules, external, depth)
  423. {
  424.   // is there any rule in the stylesheet ; earlay way out if not
  425.   if (rules && rules.length) {
  426.     var subtreechildren = document.createElementNS(XUL_NS, "treechildren");
  427.     var j, o;
  428.     var subtreeitem, subtreerow, subtreecell;
  429.     // let's browse all the rules
  430.     for (j=0; j< rules.length; j++) {
  431.       switch (rules[j].type) {
  432.         case CSSRule.STYLE_RULE:
  433.           // this is a CSSStyleRule
  434.           subtreeitem  = AddStyleRuleToTreeChildren(rules[j], external, depth);
  435.           subtreechildren.appendChild(subtreeitem);
  436.           break;
  437.         case CSSRule.PAGE_RULE:
  438.           // this is a CSSStyleRule
  439.           subtreeitem  = AddPageRuleToTreeChildren(rules[j], external, depth);
  440.           subtreechildren.appendChild(subtreeitem);
  441.           break;
  442.         case CSSRule.IMPORT_RULE:
  443.           // this is CSSImportRule for @import
  444.           subtreeitem  = AddImportRuleToTreeChildren(rules[j], external, depth);
  445.           subtreechildren.appendChild(subtreeitem);
  446.           break;
  447.         case CSSRule.MEDIA_RULE:
  448.           // this is a CSSMediaRule @media
  449.           subtreeitem  = document.createElementNS(XUL_NS, "treeitem");
  450.           subtreerow   = document.createElementNS(XUL_NS, "treerow");
  451.           subtreecell  = document.createElementNS(XUL_NS, "treecell");
  452.           // show "@media" and media list
  453.           subtreecell.setAttribute("label", "@media "+rules[j].media.mediaText, external);
  454.           o = newObject( subtreeitem, external, MEDIA_RULE, rules[j], false, depth );
  455.           PushInObjectsArray(o);
  456.           if (external) {
  457.             subtreecell.setAttribute("properties", "external");
  458.           }
  459.           subtreerow.appendChild(subtreecell);
  460.           subtreeitem.appendChild(subtreerow);
  461.           subtreeitem.setAttribute("container", "true");
  462.           // let's browse the rules attached to this CSSMediaRule, keeping the
  463.           // current external and increasing depth
  464.           AddRulesToTreechildren(subtreeitem, rules[j].cssRules, external, depth+1);
  465.           subtreechildren.appendChild(subtreeitem);
  466.           break;
  467.         case CSSRule.CHARSET_RULE:
  468.           // this is a CSSCharsetRule
  469.           subtreeitem  = document.createElementNS(XUL_NS, "treeitem");
  470.           subtreerow   = document.createElementNS(XUL_NS, "treerow");
  471.           subtreecell  = document.createElementNS(XUL_NS, "treecell");
  472.           // show "@charset" and the encoding
  473.           subtreecell.setAttribute("label", "@charset "+rules[j].encoding, external);
  474.           o = newObject( subtreeitem, external, CHARSET_RULE, rules[j], false, depth );
  475.           PushInObjectsArray(o);
  476.  
  477.  
  478.           if (external) {
  479.             subtreecell.setAttribute("properties", "external");
  480.           }
  481.           subtreerow.appendChild(subtreecell);
  482.           subtreeitem.appendChild(subtreerow);
  483.           subtreechildren.appendChild(subtreeitem);
  484.           break;          
  485.       }
  486.     }
  487.     treeItem.appendChild(subtreechildren);
  488.   }
  489. }
  490.  
  491. function GetSelectedItemData()
  492. {
  493.   // get the selected tree item (if any)
  494.   var selectedItem =  getSelectedItem(gDialog.sheetsTree);
  495.   gDialog.selectedIndex  = -1;
  496.   gDialog.selectedObject = null;
  497.  
  498.   if (!objectsArray)
  499.     return;
  500.   // look for the object in objectsArray corresponding to the
  501.   // selectedItem
  502.   var i, l = objectsArray.length;
  503.   if (selectedItem) {
  504.     for (i=0; i<l; i++) {
  505.       if (objectsArray[i].xulElt == selectedItem) {
  506.         gDialog.selectedIndex  = i;
  507.         gDialog.treeItem       = objectsArray[i].xulElt;
  508.         gDialog.externalObject = objectsArray[i].external;
  509.         gDialog.selectedType   = objectsArray[i].type;
  510.         gDialog.selectedObject = objectsArray[i].cssElt;
  511.         gDialog.modifiedObject = objectsArray[i].modified;
  512.         gDialog.depthObject    = objectsArray[i].depth;
  513.         break;
  514.       }
  515.     }
  516.   }
  517. }
  518.  
  519. // * selects either a tree item (sheet, rule) or a tab
  520. //   in the former case, the parameter is null ; in the latter,
  521. //   the parameter is a string containing the name of the selected tab
  522. //   param String tab
  523. function onSelectCSSTreeItem(tab)
  524. {
  525.   // convert the tab string into a tab id
  526.   if      (tab == "general")       tab = GENERAL_TAB;
  527.   else if (tab == "text")          tab = TEXT_TAB;
  528.   else if (tab == "background")    tab = BACKGROUND_TAB;
  529.   else if (tab == "border")        tab = BORDER_TAB;
  530.   else if (tab == "box")           tab = BOX_TAB;
  531.   else if (tab == "aural")         tab = AURAL_TAB;
  532.  
  533.   GetSelectedItemData();
  534.  
  535.   if (gDialog.selectedIndex == -1) {
  536.     // there is no tree item selected, let's fallback to the Info tab
  537.     // but there is nothing we can display in that tab...
  538.     gDialog.sheetTabbox.selectedTab = gDialog.sheetInfoTab;
  539.     return;
  540.   }
  541.  
  542.   var external  = objectsArray[gDialog.selectedIndex].external;
  543.   var type      = objectsArray[gDialog.selectedIndex].type;
  544.   var cssObject = gDialog.selectedObject;
  545.   // Let's update the buttons depending on what kind of object is
  546.   // selected in the tree
  547.   UpdateButtons(!external, !external, true, true, !external, (!external || (type == SHEET)) );
  548.  
  549.   if (gDialog.selectedType != STYLE_RULE) {
  550.     // user did not select a CSSStyleRule, let's fallback to Info tab
  551.     tab = GENERAL_TAB;
  552.   }
  553.   if (!tab) {
  554.     // this method gets called by a selection in the tree. Is there a
  555.     // tab already selected ? If yes, keep it; if no, fallback to the
  556.     // Info tab
  557.     tab = gDialog.selectedTab ? gDialog.selectedTab : GENERAL_TAB;
  558.   }
  559.   switch (tab) {
  560.     case TEXT_TAB:
  561.       // we have to update the text preview, let's remove its style attribute
  562.       gDialog.brownFoxLabel.removeAttribute("style");      
  563.       InitTextTabPanel();
  564.       return;
  565.       break;
  566.     case BACKGROUND_TAB:
  567.       InitBackgroundTabPanel();
  568.       return;
  569.       break;
  570.     case BORDER_TAB:
  571.       InitBorderTabPanel();
  572.       return;
  573.       break;
  574.     case BOX_TAB:
  575.       InitBoxTabPanel();
  576.       return;
  577.       break;
  578.     case AURAL_TAB:
  579.       InitAuralTabPanel();
  580.       return;
  581.       break;
  582.   }
  583.  
  584.   // if we are here, the Info tab is our choice
  585.   gDialog.selectedTab = GENERAL_TAB;
  586.   gDialog.sheetTabbox.selectedTab = gDialog.sheetInfoTab;
  587.   
  588.   var gridrows = gDialog.sheetInfoTabGridRows;
  589.   var grid     = gDialog.sheetInfoTabGrid;
  590.  
  591.   if (gridrows) {
  592.     // first, remove all information present in the Info tab
  593.     grid.removeChild(gridrows);
  594.   }
  595.  
  596.   gridrows = document.createElementNS(XUL_NS, "rows");
  597.   gDialog.sheetInfoTabGridRows = gridrows;
  598.   gridrows.setAttribute("id", "sheetInfoTabGridRows");
  599.   grid.appendChild(gridrows);
  600.   grid.removeAttribute("style");
  601.  
  602.   switch (type) {
  603.     case OWNER_NODE:
  604.       // this case is a workaround when we have a LINK element but the
  605.       // corresponding stylesheet is not loaded yet
  606.       if (gDialog.selectedObject.sheet) {
  607.         var index = gDialog.selectedIndex;
  608.         sheetLoadedTimeoutCallback(index);
  609.         onSelectCSSTreeItem(tab);
  610.         return;
  611.       }
  612.       break;
  613.     case SHEET:
  614.       if (cssObject) {
  615.         var alternate = "";
  616.         if (external &&
  617.             cssObject.ownerNode.getAttribute("rel").toLowerCase().indexOf("alternate") != -1) {
  618.           alternate = " (alternate)";
  619.         }
  620.         gDialog.sheetInfoTabPanelTitle.setAttribute("value", "Stylesheet"+alternate);
  621.         AddLabelToInfobox(gridrows, gDialog.bundle.getString("Type"), cssObject.type, null, false);
  622.         AddCheckboxToInfobox(gridrows,
  623.                              gDialog.bundle.getString("Disabled"),
  624.                              gDialog.bundle.getString("DisableStylesheet"),
  625.                              cssObject.disabled, "onStylesheetDisabledChange");
  626.         var href;
  627.         if (cssObject.ownerNode.nodeName.toLowerCase() == "link") {
  628.           href = cssObject.href;
  629.         }
  630.         else {
  631.           href = "none (embedded into the document)";
  632.         }
  633.         var cleanUrl = StripUsernamePassword(href);
  634.         if (cleanUrl == href)
  635.           AddLabelToInfobox(gridrows, "URL:", href, null, false);
  636.         else
  637.           AddLabelToInfobox(gridrows, "URL:",
  638.                             cleanUrl + " " + gDialog.bundle.getString("UserPasswdStripped"),
  639.                             null, false);
  640.         var mediaList = cssObject.media.mediaText;
  641.         if (!mediaList || mediaList == "") {
  642.           mediaList = "all";
  643.         }
  644.         AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("MediaList"),
  645.                                  mediaList, "onStylesheetMediaChange", false);
  646.         if (cssObject.title) {
  647.           AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("Title"),
  648.                                    cssObject.title, "onStylesheetTitleChange", false);
  649.         }
  650.  
  651.         if (cssObject.cssRules) {
  652.           AddLabelToInfobox(gridrows, gDialog.bundle.getString("NumberRules"),
  653.                             cssObject.cssRules.length, null, false);
  654.         }
  655.         if (!external && cssObject.ownerNode.nodeName.toLowerCase() == "style")
  656.           // we can export only embedded stylesheets
  657.           AddSingleButtonToInfobox(gridrows, gDialog.bundle.getString("ExportStylesheet"),
  658.                                    "onExportStylesheet")
  659.       }
  660.       else
  661.         AddLabelToInfobox(gridrows, gDialog.bundle.getString("UnableRetrieveStylesheet"),
  662.                           null, null, false);
  663.       break;
  664.     case STYLE_RULE:
  665.     case PAGE_RULE:
  666.       var h = document.defaultView.getComputedStyle(grid.parentNode, "").getPropertyValue("height");
  667.       // let's prepare the grid for the case the rule has a laaaaarge number
  668.       // of property declarations
  669.       grid.setAttribute("style", "overflow: auto; height: "+h);
  670.       gDialog.sheetInfoTabPanelTitle.setAttribute("value",
  671.                   (type == STYLE_RULE) ? gDialog.bundle.getString("StyleRule") :
  672.                                          gDialog.bundle.getString("PageRule"));
  673.  
  674.       AddLabelToInfobox(gridrows, gDialog.bundle.getString("Selector"),
  675.                         cssObject.selectorText, null, false);
  676.       if (cssObject.style.length) {
  677.         AddLabelToInfobox(gridrows, gDialog.bundle.getString("Declarations"),
  678.                           " ", gDialog.bundle.getString("Importance"), false);
  679.         for (var i=0; i<cssObject.style.length; i++) {
  680.           AddDeclarationToInfobox(gridrows, cssObject, i, false);
  681.         }
  682.       }
  683.       // TO BE DONE : need a way of changing the importance of a given declaration
  684.       break;
  685.     case MEDIA_RULE:
  686.       gDialog.sheetInfoTabPanelTitle.setAttribute("value", "Media rule");
  687.       AddLabelToInfobox(gridrows, gDialog.bundle.getString("MediaList"),
  688.                         cssObject.media.mediaText, null, false);
  689.       AddLabelToInfobox(gridrows, gDialog.bundle.getString("NumberRules"),
  690.                         cssObject.cssRules.length, null, false);
  691.       break;
  692.     case IMPORT_RULE:
  693.       gDialog.sheetInfoTabPanelTitle.setAttribute("value", "Import rule");
  694.       AddLabelToInfobox(gridrows, "URL:", cssObject.href, null, false);
  695.       AddLabelToInfobox(gridrows, gDialog.bundle.getString("MediaList"),
  696.                         cssObject.media.mediaText, null, false);
  697.       break;
  698.     // TO BE DONE : @charset and other exotic rules
  699.   }
  700. }
  701.  
  702. // * updates the UI buttons with the given states
  703. //   param boolean importState
  704. //   param mediaState
  705. //   param boolean linkState
  706. //   param boolean styleState
  707. //   param boolean ruleState
  708. //   param boolean removeState
  709. function UpdateButtons(importState, mediaState, linkState, styleState, ruleState, removeState)
  710. {
  711.   if (!gDialog.expertMode) {
  712.     importState = false;
  713.     mediaState = false;
  714.     linkState = false;
  715.     styleState = false;
  716.     ruleState = true;
  717.   }
  718.   if (!gDialog.selectedObject) {
  719.     importState = false;
  720.     mediaState = false;
  721.     if (gDialog.selectedIndex != -1)
  722.       ruleState = false;
  723.   }
  724.   EnableUI(gDialog.atimportButton, importState);
  725.   EnableUI(gDialog.atmediaButton, mediaState);
  726.   EnableUI(gDialog.linkButton, linkState);
  727.   EnableUI(gDialog.styleButton, styleState);
  728.   EnableUI(gDialog.ruleButton, ruleState);
  729.   EnableUI(gDialog.removeButton, removeState);
  730.   EnableUI(gDialog.upButton, removeState);
  731.   EnableUI(gDialog.downButton, removeState);
  732. }
  733.  
  734. // * adds a button to the given treerow
  735. //   param XULElement rows
  736. //   param String label
  737. //   param String callback
  738. function AddSingleButtonToInfobox(rows, label, callback)
  739. {
  740.   var row = document.createElementNS(XUL_NS, "row");
  741.   row.setAttribute("align", "center");
  742.   var spacer = document.createElementNS(XUL_NS, "spacer");
  743.   var hbox = document.createElementNS(XUL_NS, "hbox");
  744.   var button = document.createElementNS(XUL_NS, "button");
  745.   button.setAttribute("label", label);
  746.   button.setAttribute("class", "align-right");
  747.   button.setAttribute("oncommand", callback+"();");
  748.   row.appendChild(spacer);
  749.   hbox.appendChild(button);
  750.   row.appendChild(hbox);
  751.   rows.appendChild(row);
  752. }
  753.  
  754. // * adds a textarea to the given treerow, allows to assign the
  755. //   initial value and specify that it should acquire the focus
  756. //   param XULElement rows
  757. //   param String label
  758. //   param String value
  759. //   param String callback
  760. //   param boolean focus
  761. function AddEditableZoneToInfobox(rows, label, value, callback, focus)
  762. {
  763.   var labelLabel = document.createElementNS(XUL_NS, "label");
  764.   var row = document.createElementNS(XUL_NS, "row");
  765.   row.setAttribute("align", "center");
  766.   labelLabel.setAttribute("value", label);
  767.   row.appendChild(labelLabel);
  768.  
  769.   var textbox = document.createElementNS(XUL_NS, "textbox");
  770.   if (callback != "") 
  771.     textbox.setAttribute("oninput", callback+"(this)");
  772.   textbox.setAttribute("value", value);
  773.   textbox.setAttribute("size", 20);
  774.   row.appendChild(textbox);
  775.   rows.appendChild(row);
  776.   if (focus)
  777.     SetTextboxFocus(textbox);
  778.   return textbox;
  779. }
  780.  
  781. // * adds a radiogroup to the given treerow
  782. //   param XULElement rows
  783. //   param String label
  784. function AddRadioGroupToInfoBox(rows, label)
  785. {
  786.   var row = document.createElementNS(XUL_NS, "row");
  787.   row.setAttribute("align", "center");
  788.  
  789.   var labelLabel = document.createElementNS(XUL_NS, "label");
  790.   labelLabel.setAttribute("value", label);
  791.   row.appendChild(labelLabel);
  792.  
  793.   var radiogroup = document.createElementNS(XUL_NS, "radiogroup");
  794.   row.appendChild(radiogroup);
  795.   rows.appendChild(row);
  796.   return radiogroup;
  797. }
  798.  
  799. // * adds a radio button to a previously created radiogroup
  800. //   param XULElement radiogroup
  801. //   param String label
  802. //   param String callback
  803. //   param integer selectorType
  804. //   param boolean selected
  805. function AddRadioToRadioGroup(radiogroup, label, callback, selectorType, selected)
  806. {
  807.   var radio = document.createElementNS(XUL_NS, "radio");
  808.   radio.setAttribute("label",     label);
  809.   radio.setAttribute("oncommand", callback + "(" + selectorType + ");" );
  810.   if (selected)
  811.     radio.setAttribute("selected", "true");
  812.   radiogroup.appendChild(radio);
  813. }
  814.  
  815. // * adds a label and a checkbox to a given treerows
  816. //   param XULElement rows
  817. //   param String label
  818. //   param String checkboxLabel
  819. //   param String value
  820. //   param String callback
  821. function AddCheckboxToInfobox(rows, label, checkboxLabel, value, callback)
  822. {
  823.   var row = document.createElementNS(XUL_NS, "row");
  824.   row.setAttribute("align", "center");
  825.  
  826.   var labelLabel = document.createElementNS(XUL_NS, "label");
  827.   labelLabel.setAttribute("value", label);
  828.   row.appendChild(labelLabel);
  829.  
  830.   var checkbox = document.createElementNS(XUL_NS, "checkbox");
  831.   checkbox.setAttribute("label", checkboxLabel);
  832.   checkbox.setAttribute("checked", value);
  833.   checkbox.setAttribute("oncommand", callback+"()");
  834.   row.appendChild(checkbox);
  835.  
  836.   rows.appendChild(row);
  837. }
  838.  
  839. // * adds a label to a given treerows; strong indicates a bold font
  840. //   param XULElement rows
  841. //   param String label
  842. //   param String value
  843. //   param boolean strong
  844. function AddLabelToInfobox(rows, firstLabel, flexibleLabel, lastLabel, strong)
  845. {
  846.   var labelLabel = document.createElementNS(XUL_NS, "label");
  847.   var row = document.createElementNS(XUL_NS, "row");
  848.   row.setAttribute("align", "center");
  849.   labelLabel.setAttribute("value", firstLabel);
  850.   if (strong) {
  851.     labelLabel.setAttribute("class", "titleLabel");
  852.   }
  853.   row.appendChild(labelLabel);
  854.  
  855.   if (flexibleLabel) {
  856.     var valueLabel = document.createElementNS(XUL_NS, "label");
  857.     valueLabel.setAttribute("value", flexibleLabel);
  858.     if (strong) {
  859.       valueLabel.setAttribute("class", "titleLabel");
  860.     }
  861.     row.appendChild(valueLabel);
  862.   }
  863.  
  864.   if (lastLabel) {
  865.     valueLabel = document.createElementNS(XUL_NS, "label");
  866.     valueLabel.setAttribute("value", lastLabel);
  867.     if (strong) {
  868.       valueLabel.setAttribute("class", "titleLabel");
  869.     }
  870.     row.appendChild(valueLabel);
  871.   }
  872.  
  873.   rows.appendChild(row);
  874. }
  875.  
  876. // * adds a declaration's importance to a given treerows
  877. //   param XULElement rows
  878. //   param String label
  879. //   param String value
  880. //   param String importance
  881. function AddDeclarationToInfobox(rows, cssObject, i, importance)
  882. {
  883.   var labelLabel = document.createElementNS(XUL_NS, "label");
  884.   var row = document.createElementNS(XUL_NS, "row");
  885.   row.setAttribute("align", "center");
  886.   labelLabel.setAttribute("value", "");
  887.   row.appendChild(labelLabel);
  888.  
  889.   var valueLabel = document.createElementNS(XUL_NS, "label");
  890.   valueLabel.setAttribute("value", GetDeclarationText(cssObject, i));
  891.   row.appendChild(valueLabel);
  892.  
  893.   var importanceLabel = document.createElementNS(XUL_NS, "checkbox");
  894.   if (GetDeclarationImportance(cssObject, i) == "important") {
  895.     importanceLabel.setAttribute("checked", true);
  896.   }
  897.   importanceLabel.setAttribute("oncommand", "TogglePropertyImportance(\"" + cssObject.style.item(i) + "\")" );
  898.   row.appendChild(importanceLabel);
  899.  
  900.   rows.appendChild(row);
  901. }
  902.  
  903. function TogglePropertyImportance(property)
  904. {
  905.   var cssObject = gDialog.selectedObject;
  906.   var newImportance =  (cssObject.style.getPropertyPriority(property) == "important") ? "" : "important" ;
  907.   cssObject.style.setProperty(property, cssObject.style.getPropertyValue(property), newImportance);
  908. }
  909.  
  910. // * retrieves the index-nth style declaration in a rule
  911. //   param DOMCSSRule styleRule
  912. //   param integer index
  913. //   return String
  914. function GetDeclarationText(styleRule, index)
  915. {
  916.   var pName = styleRule.style.item(index);
  917.   return pName + ": " + styleRule.style.getPropertyValue(pName);
  918. }
  919.  
  920. // * retrieves the stylesheet containing the selected tree entry
  921. //   return integer
  922. function GetSheetContainer()
  923. {
  924.   var index = gDialog.selectedIndex;
  925.   while (index >= 0 && objectsArray[index].type != SHEET) {
  926.     index--;
  927.   }
  928.   return index;
  929. }
  930.  
  931. // * declares that the stylesheet containing the selected tree entry
  932. //   has been modified
  933. function SetModifiedFlagOnStylesheet()
  934. {
  935.   var index = GetSheetContainer();
  936.   if (index != -1) {
  937.     objectsArray[index].modified = true;
  938.     gDialog.modified = true;
  939.   }
  940. }
  941.  
  942. // * we are about to put some info about the selected entry into
  943. //   the Info tab
  944. //   return XULElement
  945. function PrepareInfoGridForCreation()
  946. {
  947.   gDialog.sheetTabbox.selectedTab = gDialog.sheetInfoTab;
  948.   
  949.   var gridrows = gDialog.sheetInfoTabGridRows;
  950.   var grid     = gDialog.sheetInfoTabGrid;
  951.  
  952.   if (gridrows) {
  953.     grid.removeChild(gridrows);
  954.   }
  955.  
  956.   gridrows = document.createElementNS(XUL_NS, "rows");
  957.   gDialog.sheetInfoTabGridRows = gridrows;
  958.   gridrows.setAttribute("id", "sheetInfoTabGridRows");
  959.   grid.appendChild(gridrows);
  960.   grid.removeAttribute("style");
  961.   return gridrows;
  962. }
  963.  
  964. // * user wants to create a @import rule
  965. function CreateNewAtimportRule()
  966. {
  967.   var gridrows = PrepareInfoGridForCreation();
  968.  
  969.   gDialog.newType      = IMPORT_RULE;
  970.   gDialog.newMediaList = "";
  971.   gDialog.newURL       = "";
  972.   gDialog.sheetInfoTabPanelTitle.setAttribute("value", gDialog.bundle.getString("NewImportRule"));
  973.   AddEditableZoneToInfobox(gridrows, "URL:",        gDialog.newURL,    "onNewURLChange", true);
  974.   AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("MediaList"),
  975.                            gDialog.newMediaList, "onNewMediaListChange", false);
  976.   AddSingleButtonToInfobox(gridrows, gDialog.bundle.getString("CreateImportRule"),
  977.                            "onConfirmCreateNewObject", false);
  978. }
  979.  
  980. // * user wants to create a new style rule
  981. function CreateNewStyleRule()
  982. {
  983.   var gridrows = PrepareInfoGridForCreation();
  984.  
  985.   gDialog.newExternal  = false;
  986.   gDialog.newType      = STYLE_RULE;
  987.   gDialog.newSelector  = "";
  988.   gDialog.sheetInfoTabPanelTitle.setAttribute("value", gDialog.bundle.getString("NewStyleRule"));
  989.   gDialog.newSelectorType = CLASS_SELECTOR;
  990.  
  991.   var radiogroup = AddRadioGroupToInfoBox(gridrows, gDialog.bundle.getString("CreateNew"));
  992.   // offer choice between class selector and type element selector
  993.   AddRadioToRadioGroup(radiogroup, gDialog.bundle.getString("NamedStyle"),
  994.                        "onCreationStyleRuleTypeChange", CLASS_SELECTOR, true);
  995.   AddRadioToRadioGroup(radiogroup, gDialog.bundle.getString("ElementStyle"),
  996.                        "onCreationStyleRuleTypeChange", TYPE_ELEMENT_SELECTOR, false);
  997.  
  998.   // oh, and in expert mode, allow of course any selector
  999.   if (gDialog.expertMode) {
  1000.     AddRadioToRadioGroup(radiogroup, gDialog.bundle.getString("SelectorStyle"),
  1001.                          "onCreationStyleRuleTypeChange", GENERIC_SELECTOR, false);
  1002.   }
  1003.   AddEditableZoneToInfobox(gridrows, " ", gDialog.newSelector,  "onNewSelectorChange", true);
  1004.  
  1005.   AddSingleButtonToInfobox(gridrows, gDialog.bundle.getString("CreateStyleRule"),
  1006.                            "onConfirmCreateNewObject");
  1007. }
  1008.  
  1009. // * user changed the type of style rule (s)he wants to create
  1010. //   param integer type
  1011. function onCreationStyleRuleTypeChange(type)
  1012. {
  1013.   gDialog.newSelectorType = type;
  1014. }
  1015.  
  1016. // * user wants to create a new embedded stylesheet
  1017. function CreateNewStyleElement()
  1018. {
  1019.   var gridrows = PrepareInfoGridForCreation();
  1020.  
  1021.   gDialog.newExternal  = false;
  1022.   gDialog.newType      = SHEET;
  1023.   gDialog.newMediaList = "";
  1024.   gDialog.newTitle     = "";
  1025.   gDialog.sheetInfoTabPanelTitle.setAttribute("value", gDialog.bundle.getString("NewStylesheet"));
  1026.   AddLabelToInfobox(gridrows, gDialog.bundle.getString("Type"), "text/css", null, false);
  1027.   AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("MediaList"),
  1028.                            gDialog.newMediaList, "onNewMediaListChange", true);
  1029.   AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("Title"),
  1030.                            gDialog.newTitle,  "onNewTitleChange", false);
  1031.  
  1032.   AddSingleButtonToInfobox(gridrows, gDialog.bundle.getString("CreateStylesheet"),
  1033.                            "onConfirmCreateNewObject")
  1034. }
  1035.  
  1036. // * user wants to attach an external stylesheet
  1037. function CreateNewLinkedSheet()
  1038. {
  1039.   var gridrows = PrepareInfoGridForCreation();
  1040.  
  1041.   gDialog.newExternal  = true;
  1042.   gDialog.newType      = SHEET;
  1043.   gDialog.newAlternate = false;
  1044.   gDialog.newURL       = "";
  1045.   gDialog.newMediaList = "";
  1046.   gDialog.newTitle     = "";
  1047.   gDialog.sheetInfoTabPanelTitle.setAttribute("value", gDialog.bundle.getString("NewLinkedStylesheet"));
  1048.   AddLabelToInfobox(gridrows, gDialog.bundle.getString("Type"),
  1049.                     "text/css", null, false);
  1050.   // alternate stylesheet ?
  1051.   AddCheckboxToInfobox(gridrows, gDialog.bundle.getString("Alternate"),
  1052.                        gDialog.bundle.getString("CreateAltStylesheet"),
  1053.                        gDialog.newAlternate,
  1054.                        "onNewAlternateChange");
  1055.   gDialog.URLtextbox = AddEditableZoneToInfobox(gridrows, "URL:",
  1056.                                                 gDialog.newURL,    "onNewURLChange", true);
  1057.   AddSingleButtonToInfobox(gridrows, gDialog.bundle.getString("ChooseFile"), "onChooseLocalFile")
  1058.  
  1059.   AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("MediaList"),
  1060.                            gDialog.newMediaList, "onNewMediaListChange", false);
  1061.   AddEditableZoneToInfobox(gridrows, gDialog.bundle.getString("Title"),
  1062.                            gDialog.newTitle,  "onNewTitleChange", false);
  1063.  
  1064.   AddSingleButtonToInfobox(gridrows, gDialog.bundle.getString("CreateStylesheet"),
  1065.                            "onConfirmCreateNewObject")
  1066.  
  1067.   // the two following labels are unfortunately useful...
  1068.   AddLabelToInfobox(gridrows, "", gDialog.bundle.getString("SaveBeforeLocalSheetWarning1"), null, false);
  1069.   AddLabelToInfobox(gridrows, "", gDialog.bundle.getString("SaveBeforeLocalSheetWarning2"), null, false);
  1070. }
  1071.  
  1072. // * forget about everything, and let's redo it
  1073. function Restart()
  1074. {
  1075.   // delete all objects we keep track of
  1076.   var l = objectsArray.length;
  1077.   for (var i=0; i<l; i++) {
  1078.     delete objectsArray[i];
  1079.   }
  1080.   delete objectsArray;
  1081.  
  1082.   // now, let's clear the tree
  1083.   var gridrows = gDialog.sheetInfoTabGridRows;
  1084.   if (gridrows) {
  1085.     var elt = gridrows.lastChild;
  1086.     while (elt) {
  1087.       var tmp = elt.previousSibling;
  1088.       gridrows.removeChild(elt);
  1089.       elt = tmp;
  1090.     }
  1091.   }
  1092.  
  1093.   // switch to default Info tab
  1094.   gDialog.selectedTab = GENERAL_TAB;
  1095.   gDialog.sheetInfoTabPanelTitle.setAttribute("value", "");
  1096.  
  1097.   // let's recreate the tree
  1098.   InitSheetsTree(gDialog.sheetsTreechildren);
  1099.   // and update the buttons
  1100.   UpdateButtons(false, false, true, true, false, false);
  1101. }
  1102.  
  1103. // * does less than Restart(). We only regenerate the tree, keeping track
  1104. //   of the selection
  1105. function Refresh()
  1106. {
  1107.   var index = gDialog.selectedIndex;
  1108.   Restart();
  1109.   if (index != -1)
  1110.     selectTreeItem(objectsArray[index].xulElt);
  1111. }
  1112.  
  1113. /* CALLBACKS */
  1114.  
  1115. // * user toggled the "Alternate Stylesheet" checkbox
  1116. function onNewAlternateChange()
  1117. {
  1118.   gDialog.newAlternate = !gDialog.newAlternate;
  1119. }
  1120.  
  1121. // * user changed the URL of an external stylesheet; elt is the textarea
  1122. //   param XULElement elt
  1123. function onNewURLChange(elt)
  1124. {
  1125.   gDialog.newURL = elt.value;
  1126. }
  1127.  
  1128. // * user changed the selector for a rule; elt is the textarea
  1129. //   param XULElement elt
  1130. function onNewSelectorChange(elt)
  1131. {
  1132.   gDialog.newSelector = elt.value;
  1133. }
  1134.  
  1135. // * user changed the list of medias for a new rule; elt is the textarea
  1136. //   param XULElement elt
  1137. function onNewMediaListChange(elt)
  1138. {
  1139.   gDialog.newMediaList = elt.value;
  1140. }
  1141.  
  1142. // * user changed the title of a new stylesheet; elt is the textarea
  1143. //   param XULElement elt
  1144. function onNewTitleChange(elt)
  1145. {
  1146.   gDialog.newTitle = elt.value;
  1147. }
  1148.  
  1149. // * user disables/enabled the stylesheet
  1150. function onStylesheetDisabledChange()
  1151. {
  1152.   gDialog.selectedObject.disabled = !gDialog.selectedObject.disabled;
  1153. }
  1154.  
  1155. // * user changed the title of an existing stylesheet; elt is the textarea
  1156. //   param XULElement elt
  1157. function onStylesheetTitleChange(elt)
  1158. {
  1159.   var titleText = elt.value;
  1160.   gDialog.selectedObject.ownerNode.setAttribute("title", titleText);
  1161.   SetModifiedFlagOnStylesheet();
  1162. }
  1163.  
  1164. // * user changed the list of medias of an existing stylesheet; elt is the textarea
  1165. //   param XULElement elt
  1166. function onStylesheetMediaChange(elt)
  1167. {
  1168.   var mediaText= elt.value;
  1169.   gDialog.selectedObject.ownerNode.setAttribute("media", mediaText);
  1170.   SetModifiedFlagOnStylesheet();
  1171. }
  1172.  
  1173. function GetSubtreeChildren(elt)
  1174. {
  1175.   var subtreechildren = null;
  1176.   if (!elt) return null;
  1177.   if (elt.hasChildNodes()) {
  1178.     subtreechildren = elt.lastChild;
  1179.     while (subtreechildren && subtreechildren .nodeName.toLowerCase() != "treechildren") {
  1180.       subtreechildren = subtreechildren .previousSibling;
  1181.     }
  1182.   }
  1183.   if (!subtreechildren) {
  1184.     subtreechildren = document.createElementNS(XUL_NS, "treechildren");
  1185.     elt.appendChild(subtreechildren);
  1186.   }
  1187.   return subtreechildren;
  1188. }
  1189.  
  1190. // * here, we create a new sheet or rule
  1191. function onConfirmCreateNewObject()
  1192. {
  1193.   // first, let's declare we modify the document
  1194.   gDialog.modified = true;
  1195.   var selector;
  1196.  
  1197.   // if we are requested to create a style rule in expert mode,
  1198.   // let's find the last embedded stylesheet
  1199.   if (!gDialog.expertMode && gDialog.newType == STYLE_RULE) {
  1200.     var indexLastEmbeddedStylesheet = -1;
  1201.     for (var i = objectsArray.length-1; i >= 0 ; i--) {
  1202.       if (objectsArray[i].type == SHEET && ! objectsArray[i].external) {
  1203.         indexLastEmbeddedStylesheet = i;
  1204.         break;
  1205.       }
  1206.     }
  1207.     if (indexLastEmbeddedStylesheet != -1) {
  1208.       gDialog.selectedIndex = indexLastEmbeddedStylesheet;
  1209.     }
  1210.     else {
  1211.       // there is no stylesheet ! let's create one that will contain our rule
  1212.       gDialog.newExternal  = false;
  1213.       gDialog.newMediaList = "";
  1214.       gDialog.newTitle     = "";
  1215.       gDialog.newType      = SHEET;
  1216.       var selectorType     = gDialog.newSelectorType;
  1217.       selector             = gDialog.newSelector;
  1218.       onConfirmCreateNewObject();
  1219.  
  1220.       // now, create the rule...
  1221.       gDialog.newType         = STYLE_RULE;
  1222.       gDialog.newSelectorType = selectorType;
  1223.       gDialog.newSelector     = selector;
  1224.     }
  1225.   }
  1226.   var containerIndex, sheetIndex;
  1227.   var cssObject;
  1228.   var l;
  1229.   var ruleIndex;
  1230.   var newSheetOwnerNode;
  1231.   var headNode;
  1232.   var newCssRule;
  1233.   switch (gDialog.newType) {
  1234.     case STYLE_RULE:
  1235.       if (gDialog.newSelector != "") {
  1236.         containerIndex = gDialog.selectedIndex;
  1237.         while (objectsArray[containerIndex].type != SHEET &&
  1238.                objectsArray[containerIndex].type != MEDIA_RULE)
  1239.           containerIndex--;
  1240.  
  1241.         switch (gDialog.newSelectorType) {
  1242.           case TYPE_ELEMENT_SELECTOR:
  1243.           case GENERIC_SELECTOR:
  1244.             selector = gDialog.newSelector;
  1245.             break;
  1246.           case CLASS_SELECTOR:
  1247.             selector = "." + gDialog.newSelector;
  1248.             break;
  1249.         }
  1250.         cssObject = objectsArray[containerIndex].cssElt;
  1251.         l = cssObject.cssRules.length;
  1252.         cssObject.insertRule(selector + " { }", l);
  1253.  
  1254.         if (cssObject.cssRules.length > l) {
  1255.           // hmmm, there's always the bad case of a wrong rule, dropped by the
  1256.           // parser ; that's why we need to check we really inserted something
  1257.  
  1258.           /* find inserted rule's index in objectsArray */
  1259.           var depth    = objectsArray[containerIndex].depth;
  1260.           var external = objectsArray[containerIndex].external;
  1261.  
  1262.           ruleIndex = containerIndex + 1;
  1263.           while (ruleIndex < objectsArray.length &&
  1264.                  objectsArray[ruleIndex].depth > depth) {
  1265.             ruleIndex++;
  1266.           }
  1267.           var subtreechildren = GetSubtreeChildren(objectsArray[containerIndex].xulElt);
  1268.           gInsertIndex = ruleIndex;
  1269.           var subtreeitem = AddStyleRuleToTreeChildren(cssObject.cssRules[l], external, depth);
  1270.           subtreechildren.appendChild(subtreeitem);
  1271.           selectTreeItem(subtreeitem);
  1272.           SetModifiedFlagOnStylesheet();
  1273.         }
  1274.       }
  1275.       break;
  1276.     case IMPORT_RULE:
  1277.       if (gDialog.newURL != "") {
  1278.  
  1279.         containerIndex     = GetSheetContainer();
  1280.         // **must** clear the selection before changing the tree
  1281.         ClearTreeSelection(gDialog.sheetsTree);
  1282.  
  1283.         var containerCssObject = objectsArray[containerIndex].cssElt;
  1284.         var containerDepth     = objectsArray[containerIndex].depth;
  1285.         var containerExternal  = objectsArray[containerIndex].external;
  1286.  
  1287.         var cssRuleIndex = -1;
  1288.         if (containerCssObject.cssRules)
  1289.           for (i=0; i < containerCssObject.cssRules.length; i++)
  1290.             if (containerCssObject.cssRules[i].type != CSSRule.IMPORT_RULE &&
  1291.                 containerCssObject.cssRules[i].type != CSSRule.CHARSET_RULE) {
  1292.               cssRuleIndex = i;
  1293.               break;
  1294.             }
  1295.         if (cssRuleIndex == -1) {
  1296.           // no rule in the sheet for the moment or only charset and import rules
  1297.           containerCssObject.insertRule('@import url("'+ gDialog.newURL + '") ' + gDialog.newMediaList + ";",
  1298.                                         containerCssObject.cssRules.length);
  1299.           newCssRule = containerCssObject.cssRules[containerCssObject.cssRules.length - 1];
  1300.  
  1301.           subtreechildren = GetSubtreeChildren(objectsArray[containerIndex].xulElt);
  1302.  
  1303.           gInsertIndex = ruleIndex;
  1304.           subtreeitem = AddImportRuleToTreeChildren(newCssRule,
  1305.                                                     containerExternal,
  1306.                                                     containerDepth + 1);
  1307.           
  1308.           subtreechildren.appendChild(subtreeitem);
  1309.           ruleIndex = FindObjectIndexInObjectsArray(newCssRule);
  1310.         }
  1311.         else {
  1312.           // cssRuleIndex is the index of the first not charset and not import rule in the sheet
  1313.           ruleIndex = FindObjectIndexInObjectsArray(containerCssObject.cssRules[cssRuleIndex]);
  1314.           // and ruleIndex represents the index of the corresponding object in objectsArray
  1315.           var refObject  = objectsArray[ruleIndex];
  1316.  
  1317.           containerCssObject.insertRule('@import url("'+ gDialog.newURL + '") ' + gDialog.newMediaList + ";",
  1318.                                         cssRuleIndex);
  1319.           newCssRule = containerCssObject.cssRules[cssRuleIndex];
  1320.           gInsertIndex = ruleIndex;
  1321.           subtreeitem = AddImportRuleToTreeChildren(newCssRule, containerExternal, containerDepth + 1);
  1322.  
  1323.           var refNode = refObject.xulElt;
  1324.           refNode.parentNode.insertBefore(subtreeitem, refNode);
  1325.         }
  1326.  
  1327.           
  1328.         selectTreeItem(subtreeitem);
  1329.         SetModifiedFlagOnStylesheet();
  1330.         if (gAsyncLoadingTimerID)
  1331.           clearTimeout(gAsyncLoadingTimerID);
  1332.         if (!newCssRule.styleSheet)
  1333.           gAsyncLoadingTimerID = setTimeout("sheetLoadedTimeoutCallback(" + ruleIndex + ")", kAsyncTimeout);
  1334.       }
  1335.       break;
  1336.     case SHEET:
  1337.       gInsertIndex = -1;
  1338.  
  1339.       ClearTreeSelection(gDialog.sheetsTree);
  1340.  
  1341.       if (gDialog.newExternal && gDialog.newURL != "") {
  1342.         subtreeitem  = document.createElementNS(XUL_NS, "treeitem");
  1343.         var subtreerow   = document.createElementNS(XUL_NS, "treerow");
  1344.         var subtreecell  = document.createElementNS(XUL_NS, "treecell");
  1345.         subtreeitem.setAttribute("container", "true");
  1346.         subtreerow.appendChild(subtreecell);
  1347.         subtreeitem.appendChild(subtreerow);
  1348.         gDialog.sheetsTreechildren.appendChild(subtreeitem);
  1349.  
  1350.         newSheetOwnerNode = getCurrentEditor().document.createElement("link");
  1351.         newSheetOwnerNode.setAttribute("type", "text/css");
  1352.         newSheetOwnerNode.setAttribute("href", gDialog.newURL);
  1353.         if (gDialog.newAlternate) {
  1354.           newSheetOwnerNode.setAttribute("rel", "alternate stylesheet");
  1355.         }
  1356.         else {
  1357.           newSheetOwnerNode.setAttribute("rel", "stylesheet");
  1358.         }
  1359.         if (gDialog.newMediaList != "") {
  1360.           newSheetOwnerNode.setAttribute("media", gDialog.newMediaList);
  1361.         }
  1362.         if (gDialog.newTitle != "") {
  1363.           newSheetOwnerNode.setAttribute("title", gDialog.newTitle);
  1364.         }
  1365.         headNode = GetHeadElement();
  1366.         headNode.appendChild(newSheetOwnerNode);
  1367.  
  1368.         subtreecell.setAttribute("label", gDialog.newURL);
  1369.         external = true;
  1370.         if ( /(\w*):.*/.test(gDialog.newURL) ) {
  1371.           if (RegExp.$1 == "file") {
  1372.             external = false;
  1373.           }
  1374.         }
  1375.         if (external)
  1376.           subtreecell.setAttribute("properties", "external");
  1377.  
  1378.         if (!newSheetOwnerNode.sheet) {
  1379.           /* hack due to asynchronous load of external stylesheet */        
  1380.           var o = newObject( subtreeitem, external, OWNER_NODE, newSheetOwnerNode, false, 0 );
  1381.           PushInObjectsArray(o)
  1382.           if (gAsyncLoadingTimerID)
  1383.             clearTimeout(gAsyncLoadingTimerID);
  1384.           sheetIndex = objectsArray.length - 1;
  1385.           
  1386.           gAsyncLoadingTimerID = setTimeout("sheetLoadedTimeoutCallback(" + sheetIndex + ")", kAsyncTimeout);
  1387.         }
  1388.         else {
  1389.           o = newObject( subtreeitem, external, SHEET, newSheetOwnerNode.sheet, false, 0 );
  1390.           PushInObjectsArray(o)
  1391.           AddRulesToTreechildren(subtreeitem, newSheetOwnerNode.sheet.cssRules, external, 1);
  1392.         }
  1393.       }
  1394.       else if (!gDialog.newExternal) {
  1395.         newSheetOwnerNode = getCurrentEditor().document.createElement("style");
  1396.         newSheetOwnerNode.setAttribute("type", "text/css");
  1397.         if (gDialog.newMediaList != "") {
  1398.           newSheetOwnerNode.setAttribute("media", gDialog.newMediaList);
  1399.         }
  1400.         if (gDialog.newTitle != "") {
  1401.           newSheetOwnerNode.setAttribute("title", gDialog.newTitle);
  1402.         }
  1403.         headNode = GetHeadElement();
  1404.         headNode.appendChild(newSheetOwnerNode);
  1405.         AddSheetEntryToTree(gDialog.sheetsTreechildren, newSheetOwnerNode);
  1406.  
  1407.         selectTreeItem(objectsArray[objectsArray.length - 1].xulElt);
  1408.       }
  1409.       selectTreeItem(subtreeitem);
  1410.       break;
  1411.   }
  1412. }
  1413.  
  1414. // * we need that to refresh the tree after async sheet load
  1415. //   param integer index
  1416. function sheetLoadedTimeoutCallback(index)
  1417. {
  1418.   var subtreeitem = objectsArray[index].xulElt;
  1419.   gInsertIndex = index+1;
  1420.   ClearTreeSelection(gDialog.sheetsTree);
  1421.   if (objectsArray[index].type == OWNER_NODE && objectsArray[index].cssElt.sheet != null) {
  1422.     var sheet = objectsArray[index].cssElt.sheet;
  1423.     AddRulesToTreechildren(subtreeitem , sheet.cssRules, objectsArray[index].external,
  1424.                            objectsArray[index].depth+1);
  1425.     objectsArray[index].type = SHEET;
  1426.     objectsArray[index].cssElt = sheet;
  1427.   }
  1428.   else if (objectsArray[index].type == IMPORT_RULE && objectsArray[index].cssElt.styleSheet != null) {
  1429.     AddRulesToTreechildren(subtreeitem , objectsArray[index].cssElt.styleSheet.cssRules, true,
  1430.                            objectsArray[index].depth+1)
  1431.   }
  1432.   else
  1433.     return;
  1434.   selectTreeItem(subtreeitem);
  1435. }
  1436.  
  1437. // * gets the object's index corresponding to an entry in the tree
  1438. //   param XULElement object
  1439. //   return integer
  1440. function FindObjectIndexInObjectsArray(object)
  1441. {
  1442.   var i, l = objectsArray.length;
  1443.   for (i=0; i<l; i++)
  1444.     if (objectsArray[i].cssElt == object)
  1445.       return i;
  1446.   return -1;
  1447. }
  1448.  
  1449. // * removes the selected entry from the tree and deletes the corresponding
  1450. //   object ; does some magic if we remove a container like a stylesheet or
  1451. //   an @import rule to remove all the contained rules
  1452. function RemoveObject()
  1453. {
  1454.   GetSelectedItemData();
  1455.   var objectIndex = gDialog.selectedIndex;
  1456.   if (objectIndex == -1) return;
  1457.   var depth = gDialog.depthObject;
  1458.  
  1459.   var ruleIndex, i, ruleIndexInTree, toSplice;
  1460.  
  1461.   switch (gDialog.selectedType) {
  1462.     case SHEET:
  1463.       var ownerNode = gDialog.selectedObject.ownerNode;
  1464.       ownerNode.parentNode.removeChild(ownerNode);
  1465.  
  1466.       for (i=objectIndex+1; i<objectsArray.length && objectsArray[i].depth > depth; i++);
  1467.       toSplice = i - objectIndex;
  1468.       break;
  1469.  
  1470.     case IMPORT_RULE:
  1471.     case MEDIA_RULE:
  1472.       for (ruleIndexInTree=objectIndex-1; objectsArray[ruleIndexInTree].depth >= depth; ruleIndexInTree--);
  1473.  
  1474.       objectsArray[ruleIndexInTree].modified = true;
  1475.       ruleIndex = getRuleIndexInRulesList(gDialog.selectedObject.parentStyleSheet.cssRules,
  1476.                                           gDialog.selectedObject);
  1477.       if (ruleIndex != -1) {
  1478.         gDialog.selectedObject.parentStyleSheet.deleteRule(ruleIndex);
  1479.       }
  1480.       for (i=objectIndex+1; i<objectsArray.length && objectsArray[i].depth > depth; i++);
  1481.       toSplice = i - objectIndex;
  1482.       break;
  1483.  
  1484.     case STYLE_RULE:
  1485.       for (ruleIndexInTree=objectIndex-1; objectsArray[ruleIndexInTree].depth; ruleIndexInTree--);
  1486.       objectsArray[ruleIndexInTree].modified = true;
  1487.       if (gDialog.selectedObject.parentRule) {
  1488.         /* this style rule is contained in an at-rule */
  1489.         /* need to remove the rule only from the at-rule listing it */
  1490.         ruleIndex = getRuleIndexInRulesList(gDialog.selectedObject.parentRule.cssRules,
  1491.                                             gDialog.selectedObject);
  1492.         if (ruleIndex != -1) {
  1493.           gDialog.selectedObject.parentRule.deleteRule(ruleIndex);
  1494.         }
  1495.       }
  1496.       else {
  1497.         ruleIndex = getRuleIndexInRulesList(gDialog.selectedObject.parentStyleSheet.cssRules,
  1498.                                             gDialog.selectedObject);
  1499.         if (ruleIndex != -1) {
  1500.           gDialog.selectedObject.parentStyleSheet.deleteRule(ruleIndex);
  1501.         }
  1502.       }
  1503.       toSplice = 1;
  1504.       break;
  1505.   }
  1506.   // let's remove the whole treeitem
  1507.   gDialog.treeItem.parentNode.removeChild(gDialog.treeItem);
  1508.   // and then remove the objects from our array
  1509.   objectsArray.splice(objectIndex, toSplice);
  1510.   // can we select an item ?
  1511.   if (objectsArray.length)
  1512.     selectTreeItem(objectsArray[Math.min(objectIndex, objectsArray.length - 1)].xulElt);
  1513. }
  1514.  
  1515. // * moves a sheet/rule up in the tree
  1516. function MoveObjectUp()
  1517. {
  1518.   GetSelectedItemData();
  1519.   var index = gDialog.selectedIndex;
  1520.   if (index <= 0) return;
  1521.   var sheetIndex = GetSheetContainer();
  1522.   
  1523.   switch (gDialog.selectedType) {
  1524.   case SHEET:
  1525.     var ownerNode = gDialog.selectedObject.ownerNode;
  1526.     ClearTreeSelection(gDialog.sheetsTree)
  1527.     index--;
  1528.     while (index && objectsArray[index].type != SHEET)
  1529.       index--;
  1530.     if (index == -1) return;
  1531.     ownerNode.parentNode.insertBefore(ownerNode, objectsArray[index].cssElt.ownerNode);
  1532.     Restart();
  1533.     selectTreeItem(objectsArray[index].xulElt);
  1534.     gDialog.modified = true;
  1535.     break;
  1536.  
  1537.   case OWNER_NODE:
  1538.     ownerNode = gDialog.selectedObject;
  1539.     ClearTreeSelection(gDialog.sheetsTree)
  1540.     index--;
  1541.     while (index && objectsArray[index].type != SHEET)
  1542.       index--;
  1543.     if (index == -1) return;
  1544.     ownerNode.parentNode.insertBefore(ownerNode, objectsArray[index].cssElt.ownerNode);
  1545.     Restart();
  1546.     selectTreeItem(objectsArray[index].xulElt);
  1547.     gDialog.modified = true;
  1548.     break;
  1549.  
  1550.   case STYLE_RULE:
  1551.   case PAGE_RULE:
  1552.     var rule = gDialog.selectedObject;
  1553.     objectsArray[sheetIndex].modified = true;
  1554.  
  1555.     ClearTreeSelection(gDialog.sheetsTree)
  1556.     var ruleText = rule.cssText;
  1557.     var subtreeitem;
  1558.     var newRule;
  1559.     if (rule.parentRule) {
  1560.       var ruleIndex = getRuleIndexInRulesList(rule.parentRule.cssRules, rule);
  1561.       var parentRule = rule.parentRule;
  1562.       
  1563.       if (ruleIndex == -1) return;
  1564.  
  1565.       if (!ruleIndex) {
  1566.         // we have to move the rule just before its parent rule
  1567.         parentRule.deleteRule(0);
  1568.         var parentRuleIndex;
  1569.  
  1570.         parentRuleIndex = getRuleIndexInRulesList(parentRule.parentStyleSheet.cssRules, parentRule);
  1571.         parentRule.parentStyleSheet.insertRule(ruleText, parentRuleIndex);
  1572.         newRule = parentRule.parentStyleSheet.cssRules[parentRuleIndex];
  1573.       }
  1574.       else {
  1575.         // we just move the rule in its parentRule
  1576.         parentRule.deleteRule(ruleIndex);
  1577.         parentRule.insertRule(ruleText, ruleIndex - 1);
  1578.         newRule = parentRule.cssRules.item(ruleIndex - 1);
  1579.       }
  1580.       // remove the tree entry
  1581.       objectsArray[index].xulElt.parentNode.removeChild(objectsArray[index].xulElt);
  1582.       // delete the object
  1583.       objectsArray.splice(index, 1);
  1584.       // position the insertion index
  1585.       gInsertIndex = index - 1;
  1586.       subtreeitem =  AddStyleRuleToTreeChildren(newRule,
  1587.                                                 objectsArray[index-1].external,
  1588.                                                 objectsArray[index-1].depth);
  1589.       // make the new tree entry
  1590.       objectsArray[index].xulElt.parentNode.insertBefore(subtreeitem,
  1591.                                                            objectsArray[index].xulElt);
  1592.       selectTreeItem(subtreeitem);
  1593.         
  1594.     }
  1595.     else {
  1596.       // standard case, the parent of the rule is the stylesheet itself
  1597.       ruleIndex = getRuleIndexInRulesList(rule.parentStyleSheet.cssRules, rule);
  1598.       var refStyleSheet = rule.parentStyleSheet;
  1599.       if (ruleIndex == -1) return;
  1600.       if (ruleIndex) {
  1601.         // we just move the rule in the sheet
  1602.         refStyleSheet.deleteRule(ruleIndex);
  1603.         var targetRule = refStyleSheet.cssRules.item(ruleIndex - 1);
  1604.  
  1605.         if (targetRule.type == CSSRule.MEDIA_RULE) {
  1606.           targetRule.insertRule(ruleText, targetRule.cssRules.length);
  1607.           var targetRuleIndex = FindObjectIndexInObjectsArray(targetRule);
  1608.           newRule = targetRule.cssRules.item(targetRule.cssRules.length - 1);
  1609.           var subtreechildren = GetSubtreeChildren(objectsArray[targetRuleIndex].xulElt);
  1610.  
  1611.           // in this case, the target treeitem is at same location but one level deeper
  1612.           // remove the tree entry
  1613.           objectsArray[index].xulElt.parentNode.removeChild(objectsArray[index].xulElt);
  1614.           // delete the object
  1615.           objectsArray.splice(index, 1);
  1616.           // position the insertion index
  1617.           gInsertIndex = index;
  1618.           subtreeitem = AddStyleRuleToTreeChildren(newRule,
  1619.                                                    objectsArray[targetRuleIndex].external,
  1620.                                                    objectsArray[targetRuleIndex].depth + 1);
  1621.           // make the new tree entry
  1622.           subtreechildren.appendChild(subtreeitem);
  1623.           selectTreeItem(subtreeitem);
  1624.           return;
  1625.         }
  1626.         else if (targetRule.type != CSSRule.IMPORT_RULE &&
  1627.                  targetRule.type != CSSRule.CHARSET_RULE) {
  1628.           // we can move the rule before its predecessor only if that one is
  1629.           // not an @import rule nor an @charset rule
  1630.           refStyleSheet.insertRule(ruleText, ruleIndex - 1);
  1631.           newRule = refStyleSheet.cssRules[ruleIndex - 1];
  1632.           // remove the tree entry
  1633.           objectsArray[index].xulElt.parentNode.removeChild(objectsArray[index].xulElt);
  1634.           // delete the object
  1635.           objectsArray.splice(index, 1);
  1636.           // position the insertion index
  1637.           gInsertIndex = index - 1;
  1638.           subtreeitem =  AddStyleRuleToTreeChildren(newRule,
  1639.                                                     objectsArray[index-1].external,
  1640.                                                     objectsArray[index-1].depth);
  1641.           // make the new tree entry
  1642.           objectsArray[index].xulElt.parentNode.insertBefore(subtreeitem,
  1643.                                                              objectsArray[index].xulElt);
  1644.           selectTreeItem(subtreeitem);
  1645.           return;
  1646.         }
  1647.       }
  1648.       // At this point, we need to move the rule from one sheet to another one, if it
  1649.       // exists...
  1650.       // First, let's if there is another candidate stylesheet before the current one
  1651.       // for the style rule's ownership
  1652.       var refSheetIndex = FindObjectIndexInObjectsArray(refStyleSheet);
  1653.       sheetIndex = refSheetIndex;
  1654.  
  1655.       if (!sheetIndex) return; // early way out
  1656.       sheetIndex--;
  1657.       while (sheetIndex && (objectsArray[sheetIndex].type != SHEET ||
  1658.                             objectsArray[sheetIndex])) {
  1659.         sheetIndex--;
  1660.       }
  1661.       if (sheetIndex == -1) return; // no embedded or local stylesheet available
  1662.       var newStyleSheet = objectsArray[sheetIndex].cssElt;
  1663.       // we need to check the type of the last rule in the sheet, if it exists
  1664.       if (newStyleSheet.cssRules.length &&
  1665.           newStyleSheet.cssRules[newStyleSheet.cssRules.length - 1].type == CSSRule.MEDIA_RULE) {
  1666.         // this media rule is our target
  1667.         var refMediaRule      = newStyleSheet.cssRules[newStyleSheet.cssRules.length - 1];
  1668.         var refMediaRuleIndex = FindObjectIndexInObjectsArray(refMediaRule );
  1669.         var refRuleIndex      = refMediaRuleIndex++;
  1670.         while (refRuleIndex < objectsArray.length && objectsArray[refRuleIndex].depth > 1)
  1671.           refRuleIndex++;
  1672.         // refRuleIndex represents where we should insert the new object
  1673.       }
  1674.       else {
  1675.         // we just append the rule to the list of rules of the sheet
  1676.       }
  1677.     }
  1678.     break;
  1679.   }
  1680. }
  1681.  
  1682. // * moves a sheet/rule down in the tree
  1683. function MoveObjectDown()
  1684. {
  1685.   /* NOT YET IMPLEMENTED */
  1686.   var objectIndex = FindObjectIndexInObjectsArray(gDialog.selectedObject);
  1687.   if (objectIndex == -1) return;
  1688. }
  1689.  
  1690. // * opens a file picker and returns the file:/// URL in gDialog.newURL
  1691. function onChooseLocalFile() {
  1692.   // Get a local file, converted into URL format
  1693.   var fileName = getLocalFileURL(true);
  1694.   if (fileName)
  1695.   {
  1696.     gDialog.URLtextbox.setAttribute("value", fileName);
  1697.     gDialog.newURL = fileName;
  1698.   }
  1699. }
  1700.  
  1701. // * opens a file picker for a *.css filename, exports the selected stylesheet
  1702. //   in that file, and replace the selected embedded sheet by its external, but
  1703. //   local to the filesystem, new counterpart
  1704. function onExportStylesheet() {
  1705.   var fileName = getLocalFileURL(false);
  1706.   SerializeExternalSheet(gDialog.selectedObject, fileName);
  1707.   var ownerNode = gDialog.selectedObject.ownerNode;
  1708.   var newSheetOwnerNode = ownerNode.ownerDocument.createElement("link");
  1709.   newSheetOwnerNode.setAttribute("type", "text/css");
  1710.   newSheetOwnerNode.setAttribute("href", fileName);
  1711.   newSheetOwnerNode.setAttribute("rel", "stylesheet");
  1712.   var mediaList = ownerNode.getAttribute("media");
  1713.   if (mediaList && mediaList != "")
  1714.     newSheetOwnerNode.setAttribute("media", mediaList);
  1715.   ownerNode.parentNode.insertBefore(newSheetOwnerNode, ownerNode);
  1716.   ownerNode.parentNode.removeChild(ownerNode);
  1717.  
  1718.   // we still have to wait for async sheet loading's completion
  1719.   if (gAsyncLoadingTimerID)
  1720.     clearTimeout(gAsyncLoadingTimerID);
  1721.   gAsyncLoadingTimerID = setTimeout("Refresh()", 500);
  1722. }
  1723.  
  1724. function getCurrentEditor() {
  1725.   if (typeof window.InitEditorShell == "function")
  1726.     return editorShell.editor;
  1727.   else
  1728.     return GetCurrentEditor();
  1729. }
  1730.  
  1731. function AddCSSLevelChoice(rows)
  1732. {
  1733.   var labelLabel = document.createElementNS(XUL_NS, "label");
  1734.   var row = document.createElementNS(XUL_NS, "row");
  1735.   row.setAttribute("align", "center");
  1736.   labelLabel.setAttribute("value", "CSS Level");
  1737.   
  1738.   row.appendChild(labelLabel);
  1739.  
  1740.   var level;
  1741.   for (level = 1; level < 4; level++) {
  1742.     var levelCheckbox = document.createElementNS(XUL_NS, "checkbox");
  1743.     levelCheckbox.setAttribute("label", level);
  1744.     row.appendChild(levelCheckbox);
  1745.   }
  1746.  
  1747.   rows.appendChild(row);
  1748. }
  1749.